package com.bsoft.gol.hcs.followup.service;

import ctd.spring.boot.annotation.SsdevReference;
import ctd.util.AppContextHolder;
import ctd.util.BeanUtils;
import ctd.util.KeyGenerator;
import com.bsoft.gol.hcs.enumcase.FollowTypeEnum;
import com.bsoft.gol.hcs.enumcase.RouteDetailTypeEnum;
import com.bsoft.gol.hcs.followup.bean.request.ExecuteFollowUpDTO;
import com.bsoft.gol.hcs.followup.bean.request.FollowUpSatDTO;
import com.bsoft.gol.hcs.followup.bean.request.FollowUpSatDocDTO;
import com.bsoft.gol.hcs.followup.bean.response.FollowUpSatDocVO;
import com.bsoft.gol.hcs.followup.bean.response.FollowUpSatVO;
import com.bsoft.gol.hcs.followup.bean.response.FollowUpTotalSatVO;
import com.bsoft.gol.hcs.followup.dao.FusAfterHosRecordDAO;
import com.bsoft.gol.hcs.followup.entity.HospitalDischargeFollowUpEntity;
import hcn.base.Organization;
import hcn.bean.UserAllVo;
import org.atag.core.exception.ServiceException;
import org.atag.dao.model.PageQo;
import org.atag.dao.model.SimplePageInfo;
import org.atag.util.bbp.RoleUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
import pcn.enums.RoleIdEnum;
import pcn.service.web.BaseDeviceService;
import service.rpc.DoctorInfoServiceIntf;
import service.rpc.IBBPService;
import service.rpc.IOrganizationRpcService;
import com.bsoft.gol.hcs.specialist.dao.FusAfterPendingRecordDAO;
import com.bsoft.gol.hcs.specialist.dao.FusAfterRequisitionRecordDAO;
import com.bsoft.gol.hcs.specialist.entity.FusAfterHosRecord;
import com.bsoft.gol.hcs.specialist.entity.FusAfterPendingRecord;
import com.bsoft.gol.hcs.specialist.entity.FusAfterRequisitionRecord;
import com.bsoft.gol.hcs.utils.CommonUtils;
import ssdev.bbp.person.IPersonService;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;

/**
 * 院后随访服务类
 *
 * @author neoLin - 2022/05/20 14:05
 **/
@Service
public class HospitalDischargeFollowUpService {

    @Resource
    private FusAfterHosRecordDAO fusAfterHosRecordDAO;

    @Resource
    private FusAfterRequisitionRecordDAO fusAfterRequisitionRecordDAO;

    @Resource
    private FusAfterPendingRecordDAO fusAfterPendingRecordDAO;

    @SsdevReference("hcn.baseDeviceService")
    private BaseDeviceService baseDeviceService;

    @Autowired
    private IBBPService bbpService;

    @Autowired
    private IOrganizationRpcService organizationRpcService;

    @Autowired
    private DoctorInfoServiceIntf doctorInfoRpcService;


    public void checkLock(HospitalDischargeFollowUpEntity entity) {
        //检查当前是否锁定
        FusAfterHosRecord current = entity.getCurrent();
        Boolean lockFlag = current.getLockFlag();
        //已锁定检查锁定人是否为当前用户
        if (Objects.nonNull(lockFlag) && lockFlag) {
            String lockId = current.getLockId();
            UserAllVo user = baseDeviceService.getAllByOnline();
            if (Objects.isNull(user) || !lockId.equals(user.getLocalDoctorId())) {
                //todo 判断管理员
                throw new ServiceException("当前随访已被其他医生锁定");
            }
        }
    }

    public void lockRecord(HospitalDischargeFollowUpEntity entity) {
        //检查是否锁定
        checkLock(entity);
        //尝试上锁
        String recordId = entity.getRecordId();
        String lockId = entity.getFuture().getLockId();
        String lockName = entity.getFuture().getLockName();
        fusAfterHosRecordDAO.lockRecord(recordId, lockId, lockName);
        //查询是否上锁成功
        boolean lockSuccess = fusAfterHosRecordDAO.lockSuccess(recordId, lockId);
        if (!lockSuccess) {
            throw new ServiceException("随访计划锁定失败");
        }
    }

    /**
     * 解锁随访记录
     */
    public void unlockRecord(HospitalDischargeFollowUpEntity entity) {

        String recordId = entity.getRecordId();
        String lockId = entity.getFuture().getLockId();
        //检查当前是否锁定 没锁定直接返回
        boolean unLockState = fusAfterHosRecordDAO.getUnLockState(recordId);
        if (unLockState) {
            return;
        }
        //todo 检查当前解锁的人是否为上锁的人  是否是管理员
        checkLock(entity);
        fusAfterHosRecordDAO.unlockRecord(recordId, lockId);
        boolean newState = fusAfterHosRecordDAO.getUnLockState(recordId);
        if (!newState) {
            throw new ServiceException("随访计划解锁失败");
        }
    }

    /**
     * 取消随访
     */
    public void cancelFollowUp(HospitalDischargeFollowUpEntity entity) {
        checkLock(entity);
        FusAfterHosRecord update = entity.getFuture();
        update.setModifyDt(new Date());
        fusAfterHosRecordDAO.updateSelective(update);
    }

    /**
     * 恢复随访
     */
    public void recoverFollowUp(HospitalDischargeFollowUpEntity entity) {
        checkLock(entity);
        FusAfterHosRecord update = entity.getFuture();
        update.setModifyDt(new Date());
        fusAfterHosRecordDAO.updateSelective(update);
    }

    /**
     * 暂挂随访
     */
    public void hangUpFollowUp(HospitalDischargeFollowUpEntity entity, ExecuteFollowUpDTO dto) {
        checkLock(entity);
        FusAfterHosRecord update = entity.getFuture();
        update.setModifyDt(new Date());
        fusAfterHosRecordDAO.updateSelective(update);
        saveHangUpInfo(dto);
    }

    /**
     * 完成随访
     */
    public void finishFollowUp(HospitalDischargeFollowUpEntity entity, ExecuteFollowUpDTO dto) {
        //患者端完成随访不要判断锁定的情况
        if (!RoleIdEnum.PATIENT.title().equals(RoleUtils.getCurrentCommonRole())) {
            checkLock(entity);
        }
        FusAfterHosRecord update = entity.getFuture();
        update.setModifyDt(new Date());
        update.setResultCode(dto.getExecuteType());
        update.setResultCodeDetail(dto.getExecuteDetail());
        update.setExecuteId(dto.getExecuteId());
        update.setExecuteName(dto.getExecuteName());
        update.setExecuteTime(new Date());
        update.setExecuteDate(new Date());

        fusAfterHosRecordDAO.updateSelective(update);
        update = fusAfterHosRecordDAO.get(update.getRecordId());
        //门诊复诊如果前端传入问卷id不为空要替换原有的问卷
        if (CommonUtils.isNotEmpty(dto.getQuestionnaireId()) && RouteDetailTypeEnum.MZFZ.getType().equals(update.getRouteDetailType().toString())) {
            List<FusAfterRequisitionRecord> requisitionRecordList =
                    fusAfterRequisitionRecordDAO.findQuestionByRecordId(update.getRecordId());
            if (CommonUtils.isNotEmpty(requisitionRecordList)) {
                FusAfterRequisitionRecord requisition = requisitionRecordList.get(0);
                requisition.setQuestionnaireId(dto.getQuestionnaireId());
                requisition.setQuestionnaireName(dto.getQuestionnaireName());
                requisition.setUserInfoId(dto.getUserInfoId());
                requisition.setModifyDt(new Date());
                fusAfterRequisitionRecordDAO.update(requisition);
            }
        }
    }

    /**
     * 保存暂挂信息
     */
    public void saveHangUpInfo(ExecuteFollowUpDTO dto) {

        FusAfterPendingRecord insert = new FusAfterPendingRecord();
        insert.setId(KeyGenerator.randomGenerate());
        insert.setRecordId(dto.getRecordId());
        insert.setFollowType(FollowTypeEnum.FUS_AFTER.getType());
        insert.setExecuteId(dto.getExecuteId());
        insert.setExecuteName(dto.getExecuteName());
        insert.setDepartmentId(dto.getExecuteDeptId());
        insert.setDeptName(dto.getExecuteDeptName());
        insert.setPendingType(dto.getPendingType());
        insert.setPendingDetail(dto.getExecuteDetail());
        insert.setCreateDt(new Date());
        insert.setModifyDt(new Date());

        fusAfterPendingRecordDAO.insertSelective(insert);
    }

    /**
     * 随访人员统计
     */
    public SimplePageInfo<FollowUpSatDocVO> doctorFusStatistics(FollowUpSatDocDTO dto) {
        String doctorCode = dto.getDoctorId();
        //由于入参的doctorId是医生代码，要求是根据医生工号搜索，所以在全部结果里再根据医生工号过滤
        dto.setDoctorId(null);
        FollowUpTotalSatVO allFusCount = fusAfterHosRecordDAO.findAllFusCount(dto);
        SimplePageInfo<FollowUpSatDocVO> page = fusAfterHosRecordDAO.doctorFusStatistics(dto);
        List<FollowUpSatDocVO> list = page.getList();
        List<FollowUpSatDocVO> vos = dealDoctorSat(list, allFusCount, dto);
        if(CommonUtils.isNotEmpty(doctorCode) && CommonUtils.isNotEmpty(vos)){
            List<FollowUpSatDocVO>  followUpSatDocVOS = vos.stream().filter(t->CommonUtils.isNotEmpty(t.getDoctorId())&& t.getDoctorId().contains(doctorCode)).collect(Collectors.toList());
            page.setList(followUpSatDocVOS);
        }else {
            page.setList(vos);
        }
        return page;
    }

    /**
     * 随访人员统计导出列表
     */
    public List<FollowUpSatDocVO> doctorFusStatisticsExport(FollowUpSatDocDTO dto) {
        FollowUpTotalSatVO allFusCount = fusAfterHosRecordDAO.findAllFusCount(dto);
        List<FollowUpSatDocVO> list = fusAfterHosRecordDAO.doctorFusStatisticsExport(dto);
        return dealDoctorSat(list, allFusCount,dto);
    }

    /**
     * 处理人员统计数据
     */
    private List<FollowUpSatDocVO> dealDoctorSat(List<FollowUpSatDocVO> list, FollowUpTotalSatVO allFusCount, FollowUpSatDocDTO dto) {
        //初次随访总数
        Long firstFollowUpTotal = allFusCount.getFirstFollowUp();
        //再次随访总数
        Long secondFollowUpTotal = allFusCount.getSecondFollowUp();
        //随访总数
        Long followUpVisitsTotal = allFusCount.getFollowUpVisits();
        if (CollectionUtils.isEmpty(list)) {
            return Collections.emptyList();
        }
        //添加合计行
        FollowUpSatDocVO totalRow = new FollowUpSatDocVO();
        totalRow.setDoctorId("");
        totalRow.setDoctorName("");
        totalRow.setFollowUpVisits(0L);
        totalRow.setFirstFollowUp(0L);
        totalRow.setSecondFollowUp(0L);
        totalRow.setFirstFollowUpPercent("");
        totalRow.setSecondFollowUpPercent("");
        totalRow.setTotalFlag(true);

        FollowUpSatDocVO totalSum = list.stream()
                .reduce(totalRow, (v1, v2) -> {
                    FollowUpSatDocVO temp = new FollowUpSatDocVO();
                    BeanUtils.copy(v2, temp);
                    sum1(temp, v1);
                    return temp;
                });
        totalSum.setOrganizationName("合计");
        list.add(totalSum);



        Organization organization = organizationRpcService.getOrgDetailByOrgCode(dto.getOrganizationId());
       /* Map<String, String> cdMap = null;
        if(null != organization){
            cdMap = bbpService.getCdMap(organization.getOrgId());
        }
        Map<String, String> finalCdMap = cdMap;*/
        list.forEach(v -> {
            /*//医生工号
            String cd = "";
            //医生工号修改，由于查出来的doctorId是医生代码，不是医生工号，所以要去bbp那边查询医生工号
            if(null != organization && CommonUtils.isNotEmpty(v.getDoctorId())){
                cd = bbpService.getCdString(finalCdMap,organization.getOrgId(),v.getDoctorId());
                v.setDoctorId(cd);
            }*/
            if (v.getDoctorId().length()>8){
                  Map<String, Object> response = AppContextHolder.get().getBean(IPersonService.class).getByUserId(v.getDoctorId());
                  v.setDoctorId(CommonUtils.isNotEmpty(response)?(String) response.get("cd"):"");
            }
            //初次随访占总数的百分比
            String firstPercent = "0";
            if (firstFollowUpTotal != 0) {
                firstPercent = calcPer(v.getFirstFollowUp(), followUpVisitsTotal);
            }
            v.setFirstFollowUpPercent(firstPercent);
            //二次随访占总数的百分比
            String secondPercent = "0";
            if (0 != secondFollowUpTotal) {
                secondPercent = calcPer(v.getSecondFollowUp(), followUpVisitsTotal);
            }
            v.setSecondFollowUpPercent(secondPercent);
            //todo 拨入拨出数据自行计算
        });

        return list;
    }

    /**
     * 科室统计
     */
    public SimplePageInfo<FollowUpSatVO> deptFusStatistics(FollowUpSatDTO dto) {
        SimplePageInfo<FollowUpSatVO> fusStatistics = fusAfterHosRecordDAO.getFusStatistics(dto, true);
        List<FollowUpSatVO> list = fusStatistics.getList();
        List<FollowUpSatVO> newList = dealSat(list, getStart(dto), true);
        fusStatistics.setList(newList);
        return fusStatistics;
    }

    /**
     * 处理统计数据
     */
    private List<FollowUpSatVO> dealSat(List<FollowUpSatVO> list, int start, boolean groupByDept) {
        if (!CollectionUtils.isEmpty(list)) {
            list.forEach(v -> {
                if (StringUtils.isEmpty(v.getRouteId()) || "0".equals(v.getRouteId())) {
                    v.setRouteName("手动设置的随访路径");
                }
            });
            AtomicInteger atomicInteger = new AtomicInteger(start);
            //先计算分区合计值
            List<FollowUpSatVO> newList = addCountRow(list, groupByDept);
            newList.forEach(this::calc);
            newList.forEach(v -> {
                if (v.isTotalFlag()) {
                    v.setSerialNo("小计");
                    if(groupByDept){
                        v.setDepartmentName(String.valueOf(v.getSubCount()));
                    }else{
                        v.setRouteName(String.valueOf(v.getSubCount()));
                    }
                } else {
                    v.setSerialNo(Integer.toString(atomicInteger.getAndAdd(1)));
                }
            });
            //处理最后的合计数据
            FollowUpSatVO def = new FollowUpSatVO();
            def.setFollowUpVisits(0L);
            def.setCancelVisits(0L);
            def.setRealVisits(0L);
            def.setFinishVisits(0L);
            def.setFirstFollowUp(0L);
            def.setSecondFollowUp(0L);
            def.setTotalFlag(true);
            FollowUpSatVO totalSum = newList.stream()
                    .filter(FollowUpSatVO::isTotalFlag)
                    .reduce(def, (v1, v2) -> {
                        FollowUpSatVO temp = new FollowUpSatVO();
                        BeanUtils.copy(v2, temp);
                        sum(temp, v1);
                        return temp;
                    });
            totalSum.setSerialNo("合计");
            totalSum.setOrganizationName("");
            totalSum.setDepartmentName("");
            totalSum.setRouteName("");

            long orgCount = newList.stream().filter(v -> !v.isTotalFlag()).map(FollowUpSatVO::getOrganizationId).distinct().count();
            long routeCount = newList.stream().filter(v -> !v.isTotalFlag()).map(FollowUpSatVO::getRouteId).distinct().count();
            long depCount = newList.stream().filter(v -> !v.isTotalFlag()).map(FollowUpSatVO::getDepartmentId).distinct().count();
            totalSum.setOrganizationName(String.valueOf(orgCount));
            totalSum.setRouteName(String.valueOf(routeCount));
            totalSum.setDepartmentName(String.valueOf(depCount));
            newList.add(totalSum);

            return newList;
        }
        return Collections.emptyList();
    }


    private void sum1(FollowUpSatDocVO temp, FollowUpSatDocVO v1) {
        temp.setFollowUpVisits(temp.getFollowUpVisits() + v1.getFollowUpVisits());
        temp.setFirstFollowUp(temp.getFirstFollowUp() + v1.getFirstFollowUp());
        temp.setSecondFollowUp(temp.getSecondFollowUp() + v1.getSecondFollowUp());
    }

    private void sum(FollowUpSatVO temp, FollowUpSatVO v1) {
        temp.setFollowUpVisits(temp.getFollowUpVisits() + v1.getFollowUpVisits());
        temp.setCancelVisits(temp.getCancelVisits() + v1.getCancelVisits());
        temp.setFinishVisits(temp.getFinishVisits() + v1.getFinishVisits());
        temp.setFirstFollowUp(temp.getFirstFollowUp() + v1.getFirstFollowUp());
        temp.setSecondFollowUp(temp.getSecondFollowUp() + v1.getSecondFollowUp());
    }

    /**
     * 路径统计
     */
    public SimplePageInfo<FollowUpSatVO> routeFusStatistics(FollowUpSatDTO dto) {
        SimplePageInfo<FollowUpSatVO> fusStatistics = fusAfterHosRecordDAO.getFusStatistics(dto, false);
        List<FollowUpSatVO> list = fusStatistics.getList();
        List<FollowUpSatVO> newList = dealSat(list, getStart(dto), false);
        fusStatistics.setList(newList);
        return fusStatistics;
    }

    private int getStart(PageQo qo) {
        Integer pageSize = qo.getPageSize();
        Integer pageNo = qo.getPageNo();
        return (pageNo - 1) * pageSize + 1;
    }

    /**
     * 导出科室统计
     */
    public List<FollowUpSatVO> deptFusStatisticsExport(FollowUpSatDTO dto) {
        List<FollowUpSatVO> exports = fusAfterHosRecordDAO.getFusStatisticsExport(dto, true);
        return dealSat(exports, 1, true);
    }

    /**
     * 导出路径统计
     */
    public List<FollowUpSatVO> routeFusStatisticsExport(FollowUpSatDTO dto) {
        List<FollowUpSatVO> exports = fusAfterHosRecordDAO.getFusStatisticsExport(dto, false);
        return dealSat(exports, 1, false);
    }


    /**
     * 添加合计行
     */
    private List<FollowUpSatVO> addCountRow(List<FollowUpSatVO> list, boolean groupByDept) {
        List<FollowUpSatVO> newList = new ArrayList<>();
        String tempFlag = "$$";
        List<FollowUpSatVO> tempList = new ArrayList<>();
        for (FollowUpSatVO v : list) {
            String flag;
            if (groupByDept) {
                flag = v.getOrganizationId();
            } else {
                flag = v.getRouteId();
            }

            if (Objects.nonNull(flag) && !tempFlag.equals("$$") && !tempFlag.equals(flag) && !CollectionUtils.isEmpty(tempList)) {
                calcCountRow(tempList, newList);
            }
            tempFlag = Objects.isNull(flag) ? "" : flag;
            newList.add(v);
            tempList.add(v);
        }
        //循环结束处理剩下的数据
        if (!CollectionUtils.isEmpty(tempList)) {
            calcCountRow(tempList, newList);
        }
        return newList;
    }

    private void calcCountRow(List<FollowUpSatVO> tempList, List<FollowUpSatVO> newList) {
        Optional<FollowUpSatVO> reduce = tempList.stream().reduce((t1, t2) -> {
            //对某些值进行累加
//            FollowUpSatVO temp1 = new FollowUpSatVO();
            FollowUpSatVO temp2 = new FollowUpSatVO();
//            BeanUtils.copy(t1, temp1);
            BeanUtils.copy(t2, temp2);
            sum(temp2, t1);
            return temp2;
        });
        if (reduce.isPresent()) {
            FollowUpSatVO t = reduce.get();
            FollowUpSatVO countRow = new FollowUpSatVO();
            BeanUtils.copy(t, countRow);
            countRow.setSubCount(tempList.size());
            countRow.setTotalFlag(true);
            newList.add(countRow);
            tempList.clear();
        }
    }

    private void calc(FollowUpSatVO sat) {
        //随访总数
        Long followUpVisits = sat.getFollowUpVisits();
        //取消数
        Long cancelVisits = sat.getCancelVisits();
        //当日完成数
        Long finishVisits = sat.getFinishVisits();
        //再次随访数
        Long secondFollowUp = sat.getSecondFollowUp();
        //初次随访次数
        Long firstFollowUp = sat.getFirstFollowUp();
        //实际完成数
        Long realNum = followUpVisits - cancelVisits;
        sat.setRealVisits(realNum);
        //完成率 = 实际随访人数/需要随访人数
        String finishPer;
        if (followUpVisits == 0) {
            finishPer = "0";
        } else {
            finishPer = calcPer(realNum, followUpVisits);
        }
        sat.setFinishPercent(finishPer);
        //及时率=当日完成数/实际随访人数
        String inTimePer;
        if (realNum == 0) {
            inTimePer = "0";
        } else {
            //todo
            inTimePer = calcPer(finishVisits, realNum);
        }
        sat.setInTimePercent(inTimePer);
        //初次随访率=初次随访人数/真正随访人数
        String firstPer;
        if (realNum == 0) {
            firstPer = "0";
        } else {
            firstPer = calcPer(firstFollowUp, realNum);
        }
        sat.setFirstFollowUpPercent(firstPer);
        //再次随访率=再次随访人数/真正随访人数
        String secondPer;
        if (realNum == 0) {
            secondPer = "0";
        } else {
            secondPer = calcPer(secondFollowUp, realNum);
        }
        sat.setSecondFollowUpPercent(secondPer);
    }

    /**
     * 计算百分比
     *
     * @param dividend 被除数
     * @param divisor  除数
     */
    private String calcPer(Long dividend, Long divisor) {
        return BigDecimal.valueOf(dividend).divide(BigDecimal.valueOf(divisor), 2, RoundingMode.HALF_UP).multiply(new BigDecimal(100)).toPlainString();
    }

}
